import axios, { AxiosInstance, AxiosPromise, AxiosRequestConfig } from 'axios'
import { ElMessage } from 'element-plus'
import conf from '@/config'

// 扩展底层接口
interface AxiosRequestConfig_<T> extends AxiosRequestConfig<T> {
  isMock?: boolean
}

// 请求对象接口
interface ReqExecute {
  get: ReqFn,
  post: ReqFn,
  put: ReqFn,
  delete: ReqFn,
  patch: ReqFn
}
type ReqFn = (url: string, isMock?: boolean, data?: any) => AxiosPromise<any>
type Method = 'get' | 'post' | 'put' | 'delete' | 'patch'

const SERVER_ERR = '请求服务器的网址错误或网络连接失败'
const methods: Method[] = ['get', 'post', 'put', 'delete', 'patch']

class AxiosUtil {
  static axiosUtil = new AxiosUtil
  axiosInstance!: AxiosInstance // axios 实例
  request!: ReqExecute
  constructor() {
    // 初始化 request 的每个属性及对应的值
    this.request = {
      get: (): any => { }, // 函数类型中参数少的可以赋值给参数多的称为函数的斜变(参数类型相同情况下)
      post: (): any => { },
      put: (): any => { },
      delete: (): any => { },
      patch: (): any => { },
    }
    this.createAxiosInstance()
    this.beforeReqInterceptor()
    this.beforeResponseInterceptor()
    this.reqPrepare() // 给 request 对象扩展属性
  }

  // 实例化 axios 对象并配置好请求超时时间
  createAxiosInstance() {
    this.axiosInstance = axios.create({ timeout: 15000 })
  }

  // 请求开始之前的请求(成功/失败)拦截器
  beforeReqInterceptor() {
    this.axiosInstance.interceptors.request.use(request => { // 请求发送成功之前拦截器
      // 可以在这里添加 token 到请求头中
      return request
    })
  }

  // 数据响应之前的(成功/失败)拦截器
  beforeResponseInterceptor() {
    this.axiosInstance.interceptors.response.use(response => { // 成功响应后的处理函数
      const { data, msg, code } = response.data // 获取后端返回的数据
      if (code === 200) {
        return response.data
      } else if (code === 500) {
        ElMessage.error(`发生了错误 ${msg}`)
        return
      } else {
        // 后端返回的数据中 code 没有明确指定值或指定的值不存在
        ElMessage.error(`发生了未知的错误`)
        return
      }
    }, (err) => {
      // 服务器返回一个非成功的 HTTP 状态码，例如 4xx（客户端错误）或 5xx（服务器错误）执行
      ElMessage.error(SERVER_ERR)
    })
  }

  // 发送请求给服务器
  sendRequest<T = any>(options: AxiosRequestConfig_<T>) {
    // 判断当前运行环境
    if (conf.env === 'production') {
      this.axiosInstance.defaults.baseURL = conf.baseApi // 配置请求基础路径
    } else if (conf.env === 'development') {
      // 如果没有单独传递 isMock 则使用配置文件中的 isMock
      const isMock = options.isMock ?? conf.isMock
      this.axiosInstance.defaults.baseURL = isMock ? conf.mockBaseApi : conf.baseApi
    }
    return this.axiosInstance(options)
  }
  // 深入灵活应用 ts 完成请求 method 类型自动提示
  reqPrepare() {
    methods.forEach(method => {
      this.request[method] = (url, isMock, data) => {
        return this.sendRequest({
          url,
          method,
          isMock,
          data,
        })
      }
    })
  }
}




export default AxiosUtil.axiosUtil.request
